
`timescale 1ns / 1ns
//`define RV_SUB  7'b0100000

//使用32位宽时,需要删除位移指令

// `ifndef REG_BIT_WIDTH
// //定义寄存器位宽,越小,占用资源越少
// `define REG_BIT_WIDTH 16
// `define REG_BIT_WIDTH_IS16
// `endif

`ifndef REG_BIT_WIDTH
//定义寄存器位宽,越小,占用资源越少
`define REG_BIT_WIDTH 32
`endif

/*

注:有关跳转指令jalr,未完全实现(可能存在bug)

采用4级?状态机流水线
    每条RV32指令周期使用4个时钟周期,(存储指令为5个时钟周期)
    周期处理状态为

[0] 读取外部存储设备的32位数据.
    (1).复制寄存器数据到rs1,rs2 (rs1:[19:15],rs2:[24:20]),rs2根据条件会使用寄存器或者立即数
    (2).复制32位数据到instr_reg 中
[1] status被置位为2,触发exec_clk上升沿
    (1).大多数指令为累加器指令详见(modlue RxV32x). 除了位移指令外,使用了大约 400lut
[2] 读写外部存储器
    (1).读操作不会附加等待状态.
    (2).写操作需要等待1个状态.如果不等的,则下一个指向PC指令数据将不会被获取. (外部sram的问题,需要一个上升沿更新数据)
[3] 回写寄存器
    (1).rd_write不为0时,才执行以下步骤(2),(3)
    (2).REG[rd_write] <= rd;        非存储操作.
    (3).REG[rd_write] <= data_in;   读存储操作.
        注1.有关分支指令,rd_write都是0
        注2.有关跳转jalr指令,未实现

注:在[1]过程中,如果是跳转指令时,需要跳转等待,[2],[3].其中说明如下
    [2] 把PC指向rd, 把REG[rd_write]设置为PC+4
    [3] 等待sram数据更新. 和执行exec_clk(RxV32x), 把cpu_jmp_bit置为0

*/

`define REG_W (`REG_BIT_WIDTH-1)

`define RV_ADD_SUB  3'b000
`define RV_SLL      3'b001
`define RV_SLT      3'b010
`define RV_SLTU     3'b011
`define RV_XOR      3'b100
`define RV_SRL_SRA  3'b101
`define RV_OR       3'b110
`define RV_AND      3'b111

`define RV_BEQ      3'b000
`define RV_BNE      3'b001
`define RV_BLT      3'b100
`define RV_BGE      3'b101
`define RV_BLTU     3'b110
`define RV_BGEU     3'b111

`define RV_LB   3'b000
`define RV_LH   3'b001
`define RV_LW   3'b010
`define RV_LBU  3'b100
`define RV_LHU  3'b101
`define RV_SB   3'b000
`define RV_SH   3'b001
`define RV_SW   3'b010

`define RV_JAL      7'b1101111
`define RV_JALR     7'b1100111
`define RV_AUIPC    7'b0010111
`define RV_LUI      7'b0110111

`define RVI_BASE_I    7'b0010011
`define RVI_BASE_R    7'b0110011
`define RVI_STORE_L   7'b0000011
`define RVI_STORE_S   7'b0100011
`define RVI_BRANCH    7'b1100011

`define INSN_OPCODE_BIT instr_reg[6:0]
`define INSN_FUNCT_BIT  instr_reg[14:12]
`define INSN_FUNCT7_BIT instr_reg[31:25]

`define BRANCH_OFF(v)  \
    v,v,v,v, /*31:28*/ \
    v,v,v,v, /*27:24*/ \
    v,v,v,v, /*23:20*/ \
    v,v,v,v, /*19:16*/ \
    v,v,v,v  /*15:12*/ \

`define IMM12_SIG(v) \
    v,v,v,v, /*31:28*/ \
    v,v,v,v, /*27:24*/ \
    v,v,v,v, /*23:20*/ \
    v,v,v,v, /*19:16*/ \
    v,v,v,v, /*15:12*/ \
    v        /*11:11*/


`define CPU_SET_READ  \
    data_wr <= 'b0;   \
    cpu_rw_bit <= 'b1;
`define CPU_SET_WRITE \
    data_wr <= 'b1;   \
    cpu_rw_bit <= 'b1;
`define CPU_CLR_RW \
    cpu_rw_bit <= 'b0;

//`define RV_SRA  3'b101

module RxV32x(
    rs1,
    rs2,
    rd,
    rd_write,
    instr_reg,
    clk,
    cpu_jmp_bit,
    addr,
    data_wr,
    PC,
    cpu_rw_bit,
    out_debug,
);
    input wire[`REG_W:0] rs1;
    input wire[`REG_W:0] rs2;
    output reg[`REG_W:0] rd = 'd0;
    output reg[4:0]  rd_write;
    input wire[31:0] instr_reg;
    input wire clk;
    output reg cpu_jmp_bit = 'd0;
    output wire[31:0] addr; assign addr = cpu_rw_bit?cpu_rw_addr:PC;
    output reg data_wr;
    input wire[31:0]PC;
    output wire out_debug;assign out_debug = INSN_LW;

    output reg      cpu_rw_bit = 'b0;
    reg[31:0]       cpu_rw_addr = 'd0;

    wire INSN_IS_BASER =   (`INSN_OPCODE_BIT==`RVI_BASE_R);   //基础指令,操作寄存器,其实只需要判断[4:0]位
    wire INSN_IS_BASEI =   (`INSN_OPCODE_BIT==`RVI_BASE_I);   //基础指令,立即数操作
    wire INSN_IS_STORE_S = (`INSN_OPCODE_BIT==`RVI_STORE_S);
    wire INSN_IS_STORE_L = (`INSN_OPCODE_BIT==`RVI_STORE_L);
    wire INSN_IS_BRANCH =  (`INSN_OPCODE_BIT==`RVI_BRANCH);    //分支指令

    wire INSN_ADD     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_ADD_SUB));
    wire INSN_SUB     = ((INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_ADD_SUB) & `INSN_FUNCT7_BIT=='d0 );
    wire INSN_SLL     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_SLL)    );
    wire INSN_SLT     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_SLT)    );
    wire INSN_SLTU    = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_SLTU)   );
    wire INSN_XOR     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_XOR)    );
    wire INSN_SRL     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_SRL_SRA));
    wire INSN_SRA     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_SRL_SRA));
    wire INSN_OR      = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_OR)     );
    wire INSN_AND     = ((INSN_IS_BASEI | INSN_IS_BASER) &  (`INSN_FUNCT_BIT==`RV_AND)    );
    wire INSN_SB      = (INSN_IS_STORE_S&(`INSN_FUNCT_BIT==`RV_SB));
    wire INSN_SH      = (INSN_IS_STORE_S&(`INSN_FUNCT_BIT==`RV_SH));
    wire INSN_SW      = (INSN_IS_STORE_S&(`INSN_FUNCT_BIT==`RV_SW));
    wire INSN_LB      = (INSN_IS_STORE_L&(`INSN_FUNCT_BIT==`RV_LB));
    wire INSN_LH      = (INSN_IS_STORE_L&(`INSN_FUNCT_BIT==`RV_LH));
    wire INSN_LW      = (INSN_IS_STORE_L&(`INSN_FUNCT_BIT==`RV_LW));
    wire INSN_LBU     = (INSN_IS_STORE_L&(`INSN_FUNCT_BIT==`RV_LBU));
    wire INSN_LHU     = (INSN_IS_STORE_L&(`INSN_FUNCT_BIT==`RV_LHU));
    wire INSN_JAL     = (`INSN_OPCODE_BIT==`RV_JAL);
    wire INSN_JALR    = (`INSN_OPCODE_BIT==`RV_JALR);
    wire INSN_LUI     = (`INSN_OPCODE_BIT==`RV_LUI);
    wire INSN_AUIPC   = (`INSN_OPCODE_BIT==`RV_AUIPC);

    wire INSN_BEQ  = (INSN_IS_BRANCH&(`INSN_FUNCT_BIT==`RV_BEQ));
    wire INSN_BNE  = (INSN_IS_BRANCH&(`INSN_FUNCT_BIT==`RV_BNE));
    wire INSN_BLT  = (INSN_IS_BRANCH&(`INSN_FUNCT_BIT==`RV_BLT));
    wire INSN_BGE  = (INSN_IS_BRANCH&(`INSN_FUNCT_BIT==`RV_BGE));
    wire INSN_BLTU = (INSN_IS_BRANCH&(`INSN_FUNCT_BIT==`RV_BLTU));
    wire INSN_BGEU = (INSN_IS_BRANCH&(`INSN_FUNCT_BIT==`RV_BGEU));

    //reg [31:0] count = 'd0;

    always @(posedge clk) begin
        //count <= count + 1;
        if(cpu_jmp_bit)begin
            cpu_jmp_bit <= 'b0;
        end
        else
        if(cpu_rw_bit)begin
            //关闭rw位
            cpu_rw_bit <= 'd0;
        end
        else
        begin
            //执行指令
            rd_write = instr_reg[11:7];
            cpu_rw_bit  <= 'b0;
            cpu_jmp_bit <= INSN_JAL|INSN_JALR;
            case(1)
                /*加法运算都在此处*/
            INSN_ADD,
                INSN_JAL,
                INSN_JALR,
                INSN_AUIPC,
                INSN_LUI
                          :begin rd =  rs1 + rs2;  end
            INSN_SUB     : begin rd =  rs1 - rs2;  end
            INSN_SLT     : begin rd =  $signed(rs1)   <  $signed(rs2);   end
            INSN_SLTU    : begin rd =  $unsigned(rs1) < $unsigned(rs2);  end
            INSN_XOR     : begin rd =  rs1 ^ rs2;  end
            INSN_OR      : begin rd =  rs1 | rs2;  end
            INSN_AND     : begin rd =  rs1 & rs2;  end
            /*位移指令占用较多资源,使用该三条指令时,占用资源lut:1113,不使用时lut:814 */
            `ifdef REG_BIT_WIDTH_IS16
            INSN_SLL     : begin  rd = rs1 << rs2[3:0];  end
            INSN_SRL     : begin  rd = $signed(rs1)   >> rs2[3:0];  end
            INSN_SRA     : begin  rd = $unsigned(rs1) >> rs2[3:0];  end
            `endif
            /*分支指令 rd为PC,rd_write被设置为0 */
            //INSN_BNE,
            INSN_BEQ     : begin  rd_write <= 'd0;cpu_jmp_bit <= rs1==rs2;                          rd <= PC + {`BRANCH_OFF(instr_reg[31:31]),instr_reg[7:7],instr_reg[30:25],instr_reg[11:8],1'b0} - 4; end
            INSN_BNE     : begin  rd_write <= 'd0;cpu_jmp_bit <= rs1 !=rs2;                         rd <= PC + {`BRANCH_OFF(instr_reg[31:31]),instr_reg[7:7],instr_reg[30:25],instr_reg[11:8],1'b0} - 4; end
            INSN_BLT     : begin  rd_write <= 'd0;cpu_jmp_bit <= $signed(rs1) <  $signed(rs2);      rd <= PC + {`BRANCH_OFF(instr_reg[31:31]),instr_reg[7:7],instr_reg[30:25],instr_reg[11:8],1'b0} - 4; end
            INSN_BGE     : begin  rd_write <= 'd0;cpu_jmp_bit <= $signed(rs1) >= $signed(rs2);      rd <= PC + {`BRANCH_OFF(instr_reg[31:31]),instr_reg[7:7],instr_reg[30:25],instr_reg[11:8],1'b0} - 4; end
            INSN_BLTU    : begin  rd_write <= 'd0;cpu_jmp_bit <= $unsigned(rs1) <  $unsigned(rs2);  rd <= PC + {`BRANCH_OFF(instr_reg[31:31]),instr_reg[7:7],instr_reg[30:25],instr_reg[11:8],1'b0} - 4; end
            INSN_BGEU    : begin  rd_write <= 'd0;cpu_jmp_bit <= $unsigned(rs1) >= $unsigned(rs2);  rd <= PC + {`BRANCH_OFF(instr_reg[31:31]),instr_reg[7:7],instr_reg[30:25],instr_reg[11:8],1'b0} - 4; end

            INSN_SB,INSN_SW,INSN_SH:begin
                //写入到目标数据
                cpu_rw_addr <= rs1 + {`IMM12_SIG(instr_reg[31:31]),instr_reg[30:25],instr_reg[11:7]};//964
                //cpu_rw_addr <= rs1 + $signed({instr_reg[31:20]});
                //cpu_rw_addr <= rs1 + rs2;//928
                rd_write <= 'd0;
                `CPU_SET_WRITE;
            end

            INSN_LW,INSN_LH,INSN_LB,
            INSN_LHU,INSN_LBU
            :begin
                //读取数据
                cpu_rw_addr <= rs1 + rs2;
                `CPU_SET_READ;
            end

            default:begin
            end
            endcase;
        end
    end
endmodule

module RxV32a(
    clk_in,
    cpu_run,
    addr_out,
    data_in,
    data_out,
    data_w,
    rst_n,
    rv_reg,
);

    input wire clk_in;
    input wire cpu_run;
    /*x31时，输出为debug数据,其他输出寄存器数据*/
    output wire[`REG_W:0]   data_out; reg[`REG_W:0] data_outr = 'd0; assign data_out = cpu_run?data_outr:((rv_reg!=5'h1F)?REG[rv_reg]:debug);
    output reg data_w = 'b0;
    input wire[31:0]  data_in;
    input wire rst_n;
    input wire[4:0] rv_reg;     //当cpu_run为低电平时,以rv_reg输出目标寄存器的值
    output wire[31:0] addr_out;

    reg[31:0] instr_reg = 'd0;
    wire clk; assign clk = clk_in;
    reg[`REG_W:0] rs1 = 'd0,rs2 = 'd0;
    wire[`REG_W:0] rd;
    reg[`REG_W:0] REG[31:0];
    reg[31:0] PC = 'd0;
    reg[31:0] debug = 'd0;
    integer i;

    initial begin
        //初始化寄存器,该段代码只是为了测试指令
        for(i=0;i<32;i=i+1)begin
            if(i=='b1)begin
                REG[i] <= 'h12345678;
            end
            else if(i>='d10&i<='d15)begin
                REG[i] <= 'h33333333;
            end
            else begin
                REG[i] <= 'd0;
            end
        end
    end

    wire[4:0] rd_write;
    wire cpu_jmp_bit;

    reg[1:0]    status =     2'd0;

    wire cpu_clk_insn_pre = (status[0]&clk_in);
    wire cpu_clk_exec     = ((status=='d2))|
                    ((status=='d3)&clk_in&cpu_rw_bit&~data_w)|  /*读取*/
                    (clk_in&data_w)|                            /*写入*/
                    (clk_in&cpu_jmp_bit);
    wire cpu_rw_bit;

    wire rs2isimm = (data_in[6:0]==`RVI_BASE_I) | (data_in[6:0]==`RVI_STORE_L);
    wire[31:0] imm12 = {
        `IMM12_SIG(data_in[31]),
    data_in[30:20]};
    wire rv_data_w;

    RxV32x rx32x(
        .rs1(rs1),
        .rs2(rs2),
        .rd(rd),
        .rd_write(rd_write),
        .instr_reg(instr_reg),
        .clk(cpu_clk_exec),
        .cpu_jmp_bit(cpu_jmp_bit),
        .addr(addr_out),
        .data_wr(rv_data_w),
        .PC(PC),
        .cpu_rw_bit(cpu_rw_bit),
        .out_debug(out_debug)
    );

    reg LW_bit = 'b0;
    reg jmp_bit = 'b0;
    reg [31:0] jmp_reg;

    always @(posedge clk_in or negedge cpu_run) begin
        if(~cpu_run)begin
            status <= 'd0;
            PC <= 'd0;
        end
        else if(cpu_run) begin
            debug <= status;        //删除后，使用更多的lut,  删除时:828 未删除时:933
            if(cpu_jmp_bit)begin
                if(rd=='h12345678)begin $finish(); end;
                if(!jmp_bit)begin
                    if(rd_write!='d0)begin
                        REG[rd_write] <= PC + 4;
                    end
                    jmp_reg <= PC + 4;
                    jmp_bit <= 'b1;
                end
                else begin
                    //需要等待RxV32x 模块处理cpu_jmp_bit为低电平
                    jmp_bit <= 'b0;
                end
                PC <= rd;
                status <= 'd0;
            end
            else if(data_w)begin
                //sram时序问题,如果是写操作,需要等待一个周期. 如果是读操作,则不需要等待
                data_w <= 'b0;
            end
            else begin
                status <= status + 1;
                case (status)
                    0:begin
                        `ifdef INSN_DECODE_USR_CASE
                            /*使用更多lut,但不会使用BSRAM(0 SDPB)*/
                            case(data_in[6:0])
                                `RV_AUIPC       :begin rs1 <= PC;rs2 <= {data_in[31:12],12'h000};  end
                                `RV_LUI         :begin rs1 <= 'd0;rs2 <= {data_in[31:12],12'h000}; end
                                `RV_JAL         :begin rs1 <= PC; rs2 <= rs2 <= {data_in[31],data_in[19:12],data_in[20],data_in[30:21]}; end
                                `RVI_STORE_S    :begin rs1 <= PC;rs2 <= {`IMM12_SIG(data_in[31:31]),data_in[30:25],data_in[11:7]}; end
                                default:begin rs1 <= REG[data_in[19:15]]; rs2 <= rs2isimm?imm12:REG[data_in[24:20]]; end
                            endcase
                        `else
                            /*使用case时,会占用更多的lut,但会使用BSRAM(2 SDPB)*/
                            if(data_in[6:0]==`RV_AUIPC)begin
                                rs1 <= PC;
                                rs2 <= {data_in[31:12],12'h000};
                            end
                            else if(data_in[6:0]==`RV_LUI)begin
                                rs1 <= 'd0;
                                rs2 <= {data_in[31:12],12'h000};
                            end
                            else if(data_in[6:0]==`RV_JAL)begin
                                rs1 <= PC;
                                rs2 <= {data_in[31],data_in[19:12],data_in[20],data_in[30:21]};
                            end
                            else
                            begin
                                rs1 <= REG[data_in[19:15]];
                                rs2 <= rs2isimm?imm12:REG[data_in[24:20]];
                            end
                        `endif
                        instr_reg <= data_in;
                        PC <= PC + 4;
                    end
                    2:begin
                        LW_bit <= cpu_rw_bit&~rv_data_w;
                        data_w <= cpu_rw_bit&rv_data_w;;
                        if(cpu_rw_bit&rv_data_w)begin
                            data_outr <= rs2;
                        end
                    end
                    3:begin
                        if(rd_write!=0)begin
                            if(LW_bit)begin
                                REG[rd_write] <= data_in;
                            end
                            else
                                REG[rd_write] <= rd;
                        end
                        data_w <= 'b0;
                    end
                    default:;
                endcase
            end
        end
    end
endmodule

/////////////////////////////////////////////////////////////////////
//


/*
生成指令
    iverilog -DIVERILOG_BUILD RxV32.v
    vvp -n a.out -lxt2

打开波形图
    gtkwave wave.vcd

*/

module iverilog_main (
);
    reg[31:0] sram[512:0];

    wire[`REG_W:0] addr_out;
    wire[31:0] data_in; assign data_in = sram_d;
    //wire[31:0] data_in; assign data_in = sram[addr_out[31:2]];
    wire[`REG_W:0] data_out;
    reg [31:0] sram_d = 'h10002703;
    reg [7:0]  sram_pos = 'd0;
    wire data_w;

    reg clk = 'b0;
    always #5 clk=~clk;        /*200MHz, SPI_SCK = 200MHz/2*/
    integer i;

    initial begin
        $readmemh(".\\c\\test03.hex",sram);
        for(i=100;i<1024;i=i+1)sram[i] <= 'h00;
        //sram['d64] <= 'h3;
    end

    wire sram_clk = ~clk;
    // always @(negedge sram_clk) begin
    //     // $display("[%d] = %02X",sram_pos,sram[sram_pos]);
    //     // sram_pos <= sram_pos + 1;
    //     //if(sram_pos=='h1f)begin $finish(); end;
    //     if(addr_out=='h34)begin
    //         begin $finish(); end;
    //     end
    //     if(data_w)begin
    //         sram[addr_out[31:2]] <= data_out;
    //         //$display("1234");
    //     end
    //     sram_d <= sram[addr_out[31:2]];
    // end

    always @(posedge sram_clk) begin
        //下降沿更新数据
        sram_d <= #2 sram[addr_out[31:2]];
        if(data_w)begin
            $display("wd:%X,%X",addr_out,data_out);
            sram[addr_out[31:2]] <= data_out;
        end
    end

    reg[31:0] debug_w = 'd0;
    wire data_w_clk = data_w&clk;
    always @(posedge data_w_clk) begin
        if(addr_out=='h400)begin
            debug_w <= {addr_out[15:0],data_out[7:0]};
        end
    end

    reg [15:0] count = 'd0;
    always @(posedge clk ) begin
        count <= count + 1;
    end

    RxV32a rv32(
        .clk_in  (clk&(count>4)),
        .cpu_run (1'b1),
        .addr_out(addr_out),
        .data_in (data_in),
        .data_out(data_out),
        .data_w  (data_w)
    );



endmodule

module iverilog_start (
);
    iverilog_main main();
    initial
    begin
        $dumpfile("wave.vcd");
        $dumpvars(0,main);
    end
endmodule

/////////////////////////////////////////////////////////////////////